﻿/*************************************************************************************
 *
 * 文 件 名： Worker
 * 描    述： 从模板新增
 * 
 * 版    本： V1.0
 * 创 建 者： YSW
 * 创建时间： 2022-08-24 8:54:29
 * ======================================
 * 历史更新记录
 * 版本：V          修改时间：         修改人：
 * 修改内容：
 * ======================================
*************************************************************************************/

using CommonModel.Util;
using GXRTBTC.PublicClassLibrary.Model;
using GXRTBTC.PublicClassLibrary.Util;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.VisualBasic;
using NewLife.Caching;
using Newtonsoft.Json.Linq;
using System.Linq;
using Npgsql;
using RedisHelp;
using StackExchange.Redis;
using System.Collections.Generic;
using System.Data.Common;
using System.Diagnostics;
using System.Data;
using GXRTBTC.PublicClassLibrary.Redis;
using GXRTBTC.WebHisDataService.Model;
using System.Security.Claims;
using System.Collections.Concurrent;
using System.Security.AccessControl;
using GXRTBTC.PublicClassLibrary;
using System.Threading;
using Newtonsoft.Json;
using Microsoft.EntityFrameworkCore.Metadata;
using IoTSharp.Data.Taos;
using Microsoft.AspNetCore.Mvc.RazorPages;
using PageResult = GXRTBTC.PublicClassLibrary.Model.PageResult;
using Page = GXRTBTC.PublicClassLibrary.Model.Page;
using System.ComponentModel;

namespace GXRTBTC.WebHisDataService
{
    /// <summary>
    /// 后台服务
    /// 使用后台服务，让服务已启动就对Worker进行初始化
    /// </summary>
    public class MyBackgroundService : Microsoft.Extensions.Hosting.BackgroundService
    {
        /// <summary>
        /// 实例Worker这里引用只是为了让其初始化
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="worker"></param>
        public MyBackgroundService(Worker worker) { }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            return Task.CompletedTask;
        }
    }

    /// <summary>
    /// 简单sql类
    /// </summary>
    public class SqlInfo
    { 
        public string devType { get; set; }
        public string sql { get; set; }
        public int times { get; set; } = 0;
    }

    /// <summary>
    /// 后台服务
    /// </summary>
    public class Worker
    {
        #region 参数
        /// <summary>
        /// 当前节点名称
        /// 组成：节点所在IP+容器ID
        /// </summary>
        public string nodeName { get; set; } = TransForm.GetHostIPOrName() + "_" + Environment.MachineName;
        /// <summary>
        /// 日志
        /// </summary>
        public readonly ILogger mLogger;
        /// <summary>
        /// 配置
        /// </summary>
        private readonly IConfiguration mConfiguration;
        /// <summary>
        /// 副本数，默认只有自身
        /// </summary>
        private int nodeNum { get; set; } = 1;
        /// <summary>
        /// 当前副本编号
        /// </summary>
        private int nodeNo { get; set; } = 0;
        private int sqlSucc { get; set; } = 0;
        private int sqlFail { get; set; } = 0;
        /// <summary>
        /// 对象锁
        /// </summary>
        public Object syncLock { get; set; } = new object();
        /// <summary>
        /// 最小线程池数量
        /// </summary>
        private int minPoolNum { get; set; } = 10;
        /// <summary>
        /// 最大线程池数量
        /// </summary>
        private int maxPoolNum { get; set; } = 500;
        /// <summary>
        /// 数据库连接
        /// </summary>
        private DbConnection dbConnection { get; set; }
        private bool IsDbConn { get; set; } = false;
        /// <summary>
        /// 配置集
        /// </summary>
        private Dictionary<string, string> paramInfo { get; set; } = new();
        private Dictionary<string, Dictionary<string, string>> hisSetMain { get; set; } = new();
        private Dictionary<string, List<Dictionary<string, string>>> hisSetDetail { get; set; } = new();
        private Dictionary<string, DeviceInfo> cacheDeviceInfos { get; set; } = new();
        /// <summary>
        /// redis连接串
        /// </summary>
        private string redisConnString { get; set; } = "";
        /// <summary>
        /// StackExchange.Redis
        /// </summary>
        private RedisHelper redisInit { get; set; }
        /// <summary>
        /// loopAlarmPush是否启用loopAlarm参数（loopAlarmPush=false时启用）
        /// </summary>
        private static bool loopAlarmPush { get; set; } = true;
        /// <summary>
        /// 执行sql缓存
        /// </summary>
        public BlockingCollection<SqlInfo> sqlCache { get; set; } = new BlockingCollection<SqlInfo>();
        /// <summary>
        /// 执行数据库操作失败时，重新尝试的最大次数
        /// </summary>
        private int maxInsTimes { get; } = 2;
        private Dictionary<string, DbConnection> dbConnections { get; set; } = new Dictionary<string, DbConnection>();
        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="configuration"></param>
        public Worker(ILogger<Worker> logger, IConfiguration configuration)
        {
            this.mLogger = logger;
            this.mConfiguration = configuration;

            //给通用日志赋值
            CommonUtil.mLogger = logger;

            //初始化
            Init();
            mLogger.LogInformation("项目已启动");
        }

        #region 初始化
        public void Init()
        {
            //线程池配置
            ThreadPool.SetMinThreads(minPoolNum, minPoolNum);
            ThreadPool.SetMaxThreads(maxPoolNum, maxPoolNum);

            try
            {
                //redis初始化
#if DEBUG
                redisConnString = RsaEncrypt.DecryptFree(mConfiguration["RedisConnStringDebug"]);
#else
                
                redisConnString = RsaEncrypt.DecryptFree(mConfiguration["RedisConnString"]);
#endif

                mLogger.LogInformation("RedisConnString=" + redisConnString);
                redisInit = new RedisHelper(redisConnString, mLogger);
            }
            catch (Exception e)
            {
                mLogger.LogError("redis连接异常，" + e);
            }

            dbConnection = InitDb();
            InitCacheInfo();
            InitTable();
            InitThread();
        }

        /// <summary>
        /// 启动线程
        /// </summary>
        private void InitThread()
        {
            new Thread(TimerInit).Start();

            //按工位号初始化线程
            InitHisThread();

            new Thread(StoreData).Start();
            new Thread(TimerTask).Start();
        }

        /// <summary>
        /// 按工位号初始化线程
        /// </summary>
        private void InitHisThread()
        {
            if (hisSetMain != null && hisSetMain.Count > 0)
            {
                foreach (string positionId in hisSetMain.Keys)
                {
                    bool used = bool.Parse(hisSetMain[positionId]["used"]);
                    string devType = hisSetMain[positionId]["dev_type"];
                    string tableName = "his_" + positionId.ToLower();

                    //每个类型分配一个数据库连接
                    if (!dbConnections.ContainsKey(devType))
                    {
                        dbConnections.TryAdd(devType, InitDb());
                    }

                    if (used && hisSetDetail.ContainsKey(devType))
                    {
                        new Thread((ThreadStart)(delegate { DealHisData(positionId); })).Start();
                    }
                }
            }
        }

        /// <summary>
        /// 数据库初始化
        /// </summary>
        private DbConnection InitDb()
        {
            DbConnection? dbconn = null;
            try
            {
                //添加解密
                dbconn = new NpgsqlConnection(RsaEncrypt.DecryptFree(mConfiguration["PgSQLConnString"]));
                dbconn.Open();
                IsDbConn = true;
                mLogger.LogInformation("pgSQL数据库连接成功！");
            }
            catch (Exception e)
            {
                IsDbConn = false;
                mLogger.LogError("pgSQL数据库连接失败！" + e.Message);
            }

            return dbconn;
        }

        /// <summary>
        /// 检查数据库连接
        /// </summary>
        /// <param name="dbConnection"></param>
        private void CheckDbState(DbConnection dbConnection)
        {
            try
            {
                DbUtil.ExecReader(dbConnection, "select 1 col1", false);
                if (dbConnection.State != ConnectionState.Open)
                {
                    dbConnection.Close();
                    dbConnection.Open();
                }
                IsDbConn = true;
            }
            catch (Exception e)
            {
                IsDbConn = false;
                mLogger.LogError("数据库连接失败！" + e.Message);
            }
        }

        /// <summary>
        /// redis缓存信息初始化
        /// </summary>
        private void InitCacheInfo()
        {
            try
            {
                Dictionary<string, Dictionary<string, string>> tmpHisSetMain = redisInit.StringGet<Dictionary<string, Dictionary<string, string>>>("hisSetMain");
                hisSetMain = (tmpHisSetMain != null && tmpHisSetMain.Count > 0) ? tmpHisSetMain : hisSetMain;

                Dictionary<string, List<Dictionary<string, string>>> tmpHisSetDetail = redisInit.HashGetAllDict<List<Dictionary<string, string>>>("hisSetDetail");
                hisSetDetail = (tmpHisSetDetail != null && tmpHisSetDetail.Count > 0) ? tmpHisSetDetail : hisSetDetail;

                loopAlarmPush = !(paramInfo.ContainsKey("loopAlarmPush") && paramInfo["loopAlarmPush"] != null && !"".Equals(paramInfo["loopAlarmPush"]) && "1".Equals(paramInfo["loopAlarmPush"]));

                Dictionary<string, DeviceInfo> tmpDeviceInfos = redisInit.HashGetAllDict<DeviceInfo>("station_deviceInfos");
                cacheDeviceInfos = (tmpDeviceInfos != null && tmpDeviceInfos.Count > 0) ? tmpDeviceInfos : cacheDeviceInfos;
            }
            catch (Exception e)
            {
                mLogger.LogInformation("更新获取台站信息缓存失败，" + e);
            }
        }

        /// <summary>
        /// 初始化信息
        /// </summary>
        private void TimerInit()
        {
            Thread.Sleep(60 * 1000);
            while (true)
            {
                //redis缓存信息初始化
                InitCacheInfo();
                CheckDbState(dbConnection);
                Thread.Sleep(60 * 1000);
            }
        }

        #region 初始化表
        /// <summary>
        /// 初始化表
        /// </summary>
        private void InitTable()
        {
            try
            {
                if (!IsDbConn)
                {
                    mLogger.LogInformation("数据库未连接，无法初始化表");
                    return;
                }

                if (hisSetMain != null && hisSetMain.Count > 0)
                {
                    foreach (string positionId in hisSetMain.Keys)
                    {
                        bool used = bool.Parse(hisSetMain[positionId]["used"]);
                        if (used)
                        { 
                            string devType = hisSetMain[positionId]["dev_type"];
                            string tableName = "his_" + positionId.ToLower();
                            if (hisSetDetail.ContainsKey(devType))
                            {
                                //所有字段
                                Dictionary<string, string> columns = new();
                                //索引字段
                                List<string> indexCols = new();
                                //创建表语句
                                string createTableSql = GetCreateTableSql(tableName, hisSetDetail[devType], out columns, out indexCols);
                                //判断表再创建
                                if (!CheckTableExists(tableName))
                                {
                                    bool rs = DbUtil.ExecSql(dbConnection, createTableSql);
                                    mLogger.LogInformation("表[" + tableName + "]创建" + (rs ? "成功" : "失败") + "！");
                                    CreateIndex(tableName, indexCols);
                                }

                                //字段变动判断
                                if (CheckTableExists(tableName))
                                {
                                    Dictionary<string, string> tableColumns = GetTableColumns(tableName);
                                    foreach (string colName in columns.Keys)
                                    {
                                        string colType = GetColType(columns[colName].Split(",")[0]);
                                        string colLen = columns[colName].Split(",")[1];
                                        if (!tableColumns.ContainsKey(colName))
                                        {
                                            //新增字段
                                            DbUtil.ExecSql(dbConnection, string.Format("ALTER TABLE {0} ADD {1} {2}", tableName, colName, GetColInfo(colType, colLen)));
                                        }
                                        else
                                        {
                                            string colTypeTable = tableColumns[colName].Split(",")[0];
                                            string colLenTable = tableColumns[colName].Split(",")[1];
                                            //字段类型不一样
                                            if (!colType.Equals(colTypeTable))
                                            {
                                                //修改字段类型，先删除字段，然后新增，这样比较保险
                                                //字段删除
                                                DbUtil.ExecSql(dbConnection, string.Format("ALTER TABLE {0} DROP COLUMN {1}", tableName, colName));
                                                //新增字段
                                                DbUtil.ExecSql(dbConnection, string.Format("ALTER TABLE {0} ADD {1} {2}", tableName, colName, GetColInfo(colType, colLen)));

                                            }
                                            else if ((TransForm.IsInt(colLen) && TransForm.IsInt(colLenTable) && int.Parse(colLen) != int.Parse(colLenTable)))
                                            { 
                                                //修改字段长度
                                                DbUtil.ExecSql(dbConnection, string.Format("ALTER TABLE {0} ALTER COLUMN {1} TYPE {2}", tableName, colName, GetColInfo(colType, colLen)));
                                            }
                                        }
                                    }
                                    foreach (string colName in tableColumns.Keys)
                                    {
                                        //多余的字段删除
                                        if (!columns.ContainsKey(colName))
                                        {
                                            DbUtil.ExecSql(dbConnection, string.Format("ALTER TABLE {0} DROP COLUMN {1}", tableName, colName));
                                        }
                                    }
                                }
                                else
                                {
                                    mLogger.LogInformation("表[" + tableName + "]不存在！");
                                }
                            }
                            else
                            {
                                mLogger.LogInformation("设备类型[" + devType + "]未设置，无法创建历史表[" + tableName + "]！");
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                mLogger.LogError("初始化表异常，" + e);
            }
        }

        /// <summary>
        /// 判断表是否存在
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        private bool CheckTableExists(string tableName)
        {
            return bool.Parse(DbUtil.GetExecValue(dbConnection, "SELECT EXISTS(SELECT FROM information_schema.tables WHERE table_schema='public' AND table_name='{0}')", tableName));
        }

        /// <summary>
        /// 获取表所有字段
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        private Dictionary<string, string> GetTableColumns(string tableName)
        {
            return DbUtil.ExecReader(dbConnection, "SELECT column_name, udt_name, character_maximum_length col_len FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '{0}'", tableName).ToDictionary(d => d["column_name"], d => d["udt_name"] + "," + d.GetValueOrDefault("col_len", ""));
        }

        /// <summary>
        /// 创建表主键、索引
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="indexCols"></param>
        private void CreateIndex(string tableName, List<string> indexCols)
        {
            //主键
            bool rs = DbUtil.ExecSql(dbConnection, "ALTER TABLE " + tableName + " ADD CONSTRAINT pkey_" + tableName + " PRIMARY KEY (id)");
            mLogger.LogInformation(tableName + " 主键：" + rs);

            //批量创建索引，包含get_time时间
            foreach (string colName in indexCols)
            {
                DbUtil.ExecSql(dbConnection, string.Format("CREATE INDEX if not exists idx_{0}{1} ON {2}({3})", colName, tableName, tableName, colName));
                mLogger.LogInformation(tableName + " 索引" + colName + "：" + rs);
            }
        }

        /// <summary>
        /// 创建表语句
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="tableDetail"></param>
        /// <param name="columns"></param>
        /// <param name="indexCols"></param>
        /// <returns></returns>
        private string GetCreateTableSql(string tableName, List<Dictionary<string, string>> tableDetail, out Dictionary<string, string> columns, out List<string> indexCols)
        {
            string createTableSql = "create table if not exists {0} ({1})";

            //添加默认列
            string colsStr = "id serial not null, get_time timestamp(6) NOT NULL DEFAULT now(), data_time timestamp(6) NOT NULL DEFAULT now(), serial_no varchar(50) DEFAULT '', dev_alarm varchar(1) DEFAULT '0'";
            columns = new Dictionary<string, string>()
            {
                { "id","int4," },
                { "get_time","timestamp,"},
                { "data_time","timestamp,"},
                { "serial_no","varchar,"},
                { "dev_alarm","varchar,"}
            };
            indexCols = new List<string>()
            {
                "get_time",
                "data_time",
                "serial_no",
                "dev_alarm"
            };

            foreach (var colInfo in tableDetail)
            {
                string colName = colInfo["col_name"].ToLower();
                string colType = colInfo["col_type"].ToLower();
                string colLen = colInfo.GetValueOrDefault("col_len", "");
                bool idxIf = bool.Parse(colInfo.GetValueOrDefault("index_if", "false"));
                if (!columns.ContainsKey(colName) && !"POSITION_ID".Equals(colName))
                {
                    columns.TryAdd(colName, colType + "," + colLen);
                    colsStr += "," + colName + " " + GetColInfo(colType, colLen);
                    if (idxIf)
                    {
                        indexCols.Add(colName);
                    }
                }
            }

            return string.Format(createTableSql, tableName, colsStr);
        }

        /// <summary>
        /// 获取字段信息
        /// </summary>
        /// <param name="colType"></param>
        /// <param name="colLen"></param>
        /// <returns></returns>
        private string GetColInfo(string colType, string colLen)
        {
            string realType = GetColType(colType);
            return realType + (("varchar".Equals(realType)) ? " (" + colLen + ")" : "");
        }

        /// <summary>
        /// 类型固化
        /// </summary>
        /// <param name="colType"></param>
        /// <returns></returns>
        private string GetColType(string colType)
        {
            string realType = colType.ToLower();
            switch (colType)
            {
                case "string":
                    realType = "varchar";
                    break;
                case "enum":
                    realType = "varchar";
                    break;
                case "uint":
                    realType = "int4";
                    break;
                case "uint8":
                    realType = "int4";
                    break;
                case "uint16":
                    realType = "int4";
                    break;
                case "bool":
                    realType = "varchar";
                    break;
                case "float":
                    realType = "float8";
                    break;
                default: break;
            }

            return realType;
        }
        #endregion
        #endregion

        #region 历史数据写入
        /// <summary>
        /// 历史数据写入
        /// </summary>
        /// <param name="positionId"></param>
        private void DealHisData(string positionId)
        {
            RedisHelper redisHis = new RedisHelper(redisConnString, mLogger);
            IBatch batchHis = redisHis.CreateBatch();
            DateTime datetimePre = DateTime.Now.AddMinutes(-10);
            DevHisRecord devHisRecord = new DevHisRecord() { positionId = positionId };

            //判断间隔
            int interval = 1000;
            //计时
            Stopwatch sw;
            while (true)
            {
                sw = Stopwatch.StartNew();
                try
                {
                    bool used = bool.Parse(hisSetMain[positionId]["used"]);
                    int saveType = int.Parse(hisSetMain[positionId]["save_type"]);
                    string devType = hisSetMain[positionId]["dev_type"];
                    string stationId = positionId.Split("_")[0];
                    int saveCount = int.Parse(hisSetMain[positionId]["save_count"]);
                    devHisRecord.saveType = saveType;
                    devHisRecord.devType = devType;
                    devHisRecord.saveCount = saveCount;

                    //台站数据，包含设备数据和告警数据等
                    HisDataCache hisDataCache = GetDevData(redisHis, batchHis, positionId, stationId);
                    if (used && hisSetDetail.ContainsKey(devType) && hisDataCache.devDatas != null && hisDataCache.devDatas.Count > 0)
                    {
                        DateTime dateTime = GetLatestTime(hisDataCache.devDatas);
                        //时间是否有有变化
                        if (dateTime != (default(DateTime)) && dateTime > datetimePre)
                        {
                            //判断是否告警
                            bool devAlarm = CheckDevAlarm(hisDataCache.stationAlarm, positionId);
                            string tableName = "his_" + positionId.ToLower();

                            //拼接sql
                            SaveInfo saveInfo = GetSaveInfo(tableName, positionId, devAlarm, dateTime, hisDataCache.devDatas, hisSetDetail[devType]);
                            devHisRecord.saveInfoAdd(saveInfo);
                            devHisRecord.timeData = dateTime;

                            //sql入缓存
                            SaveDevHis(devHisRecord);
                        }
                        datetimePre = dateTime;
                    }
                }
                catch { }
                finally
                {
                    int gap = (int)sw.ElapsedMilliseconds;
                    Thread.Sleep(gap >= interval ? 0 : (interval - gap));
                }
            }
        }

        /// <summary>
        /// 数据写入sql缓存
        /// </summary>
        /// <param name="devHisRecord"></param>
        private void SaveDevHis(DevHisRecord devHisRecord)
        {
            //所有
            if (devHisRecord.saveType == 0)
            {
                devHisRecord.timeNew = devHisRecord.timeData;
                lock (sqlCache)
                {
                    sqlCache.Add(new SqlInfo() { sql = devHisRecord.saveInfos.Last().insSql, devType = devHisRecord.devType });
                }
                devHisRecord.saveInfos.Last().hasSave = true;
            }
            //每分钟
            else if (devHisRecord.saveType == 1)
            {
                //间隔小于1分钟，不保存
                if ((devHisRecord.timeData - devHisRecord.timeNew).TotalMinutes < 1 && devHisRecord.saveInfos.Count > 1)
                {
                    return;
                }
                devHisRecord.timeNew = devHisRecord.timeData.AddSeconds(-devHisRecord.timeData.Second);
                lock (sqlCache)
                {
                    sqlCache.Add(new SqlInfo() { sql = devHisRecord.saveInfos.Last().insSql, devType = devHisRecord.devType });
                }
                devHisRecord.saveInfos.Last().hasSave = true;
            }
            //每10分钟
            else if (devHisRecord.saveType == 2)
            {
                //间隔小于60分钟，不保存
                if ((devHisRecord.timeData - devHisRecord.timeNew).TotalMinutes < 10 && devHisRecord.saveInfos.Count > 1)
                {
                    return;
                }
                //devHisRecord.timeNew = devHisRecord.timeData.AddSeconds(-devHisRecord.timeData.Second).AddMinutes(-devHisRecord.timeData.Minute);
                devHisRecord.timeNew = devHisRecord.timeData.AddSeconds(-devHisRecord.timeData.Second);
                lock (sqlCache)
                {
                    sqlCache.Add(new SqlInfo() { sql = devHisRecord.saveInfos.Last().insSql, devType = devHisRecord.devType });
                }
                devHisRecord.saveInfos.Last().hasSave = true;
            }
            //故障前后存储，正常时每小时存
            else if (devHisRecord.saveType == 3)
            {
                //整点保存数据，整点准时保存一条记录 记录是否已保存标志
                if (devHisRecord.saveInfos.Count > 1)
                {
                    if (devHisRecord.saveInfos.Last().hour != devHisRecord.saveInfos[devHisRecord.saveInfos.Count - 2].hour)
                    {
                        lock (sqlCache)
                        {
                            sqlCache.Add(new SqlInfo() { sql = devHisRecord.saveInfos.First().insSql, devType = devHisRecord.devType });
                        }
                        devHisRecord.saveInfos.First().hasSave = true;
                    }
                }

                //整点保存数据，整点准时保存一条记录 记录是否已保存标志
                if (devHisRecord.saveInfos.Count == 1 && devHisRecord.saveInfos[0].alarm)
                {
                    lock (sqlCache)
                    {
                        sqlCache.Add(new SqlInfo() { sql = devHisRecord.saveInfos.First().insSql, devType = devHisRecord.devType });
                    }
                    devHisRecord.saveInfos.First().hasSave = true;
                    //第一条即为故障时，保存接下来15条
                    devHisRecord.numSave = devHisRecord.saveCount;
                }

                //默认save只是在故障发生时为true
                //前后有故障跳变时，保存之前的15条和接下来的15条
                if (devHisRecord.saveInfos.Count > 1 && devHisRecord.saveInfos.Last().alarm != devHisRecord.saveInfos[devHisRecord.saveInfos.Count - 2].alarm)
                {
                    //不一致时，保存接下来15条
                    devHisRecord.numSave = devHisRecord.saveCount;
                    int start = 0;
                    //保存当次异常前15条记录
                    if (devHisRecord.saveInfos.Count > devHisRecord.numSave)
                    {
                        start = devHisRecord.saveInfos.Count - devHisRecord.numSave - 1;
                    }
                    for (int n = start; n < devHisRecord.saveInfos.Count; n++)
                    {
                        if (!devHisRecord.saveInfos[n].hasSave)
                        {
                            lock (sqlCache)
                            {
                                sqlCache.Add(new SqlInfo() { sql = devHisRecord.saveInfos[n].insSql, devType = devHisRecord.devType });
                            }
                            devHisRecord.saveInfos[n].hasSave = true;
                        }
                    }
                }
                else if (devHisRecord.numSave > 0)  //异常后保存后续15条记录
                {
                    //有可能已经在整点时保存过了
                    if (!devHisRecord.saveInfos.Last().hasSave)
                    {
                        lock (sqlCache)
                        {
                            sqlCache.Add(new SqlInfo() { sql = devHisRecord.saveInfos.Last().insSql, devType = devHisRecord.devType });
                        }
                        devHisRecord.saveInfos.Last().hasSave = true;
                    }
                    devHisRecord.numSave--;
                }
            }
        }

        /// <summary>
        /// 获取存储信息
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="positionId"></param>
        /// <param name="devAlarm"></param>
        /// <param name="dateTime"></param>
        /// <param name="devDatas"></param>
        /// <param name="tableDetail"></param>
        /// <returns></returns>
        private SaveInfo GetSaveInfo(string tableName, string positionId, bool devAlarm, DateTime dateTime, Dictionary<string, string> devDatas, List<Dictionary<string, string>> tableDetail)
        {
            SaveInfo saveInfo = new SaveInfo() { alarm = devAlarm, hour = DateTime.Now.Hour };
            try
            {
                string insColumn = "";
                string insValues = "";
                //0：udp数据，1：json数据
                string jsonFlag = devDatas.GetValueOrDefault("json_flag", "0");
                foreach (var colInfo in tableDetail)
                {
                    string colName = colInfo.GetValueOrDefault("col_name", "");
                    //数据来源不同，参数码就不同
                    string parno = "0".Equals(jsonFlag) ? colInfo.GetValueOrDefault("para_no", "") : colInfo.GetValueOrDefault("para_no_json", "");
                    string colType = colInfo.GetValueOrDefault("col_type", "string");
                    string colVal = GetVal(colType, devDatas.GetValueOrDefault(parno, ""));

                    insColumn = string.Format(insColumn + "{0}{1}", ",", colName);
                    insValues = string.Format(insValues + "{0}'{1}'", ",", colVal);
                }

                string serialNo = "";
                if (cacheDeviceInfos.ContainsKey(positionId))
                {
                    serialNo = cacheDeviceInfos[positionId].seviceSN;
                }

                saveInfo.insSql = string.Format("insert into {0}(data_time,serial_no,dev_alarm{1}) values('{2}','{3}','{4}'{5})", tableName, insColumn, TransForm.DateTimeFormat(dateTime), serialNo, (devAlarm ? 1 : 0), insValues);
            }
            catch (Exception e)
            {
                mLogger.LogError("sql拼接异常，" + e.Message);
            }

            return saveInfo;
        }

        /// <summary>
        /// 获取值
        /// </summary>
        /// <param name="type"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        private string GetVal(string type, string val)
        {
            string rs;
            val = val.Replace("\r", "").Replace(",", "，");
            val = val.Trim();
            if ("".Equals(val))
            {
                rs = "";
            }
            //else if ("FLOAT".Equals(type)|| "FLOAT8".Equals(type))
            if ("FLOAT".Equals(type)|| "FLOAT8".Equals(type))
            {
                if (TransForm.IsDecimalSign(val))
                {
                    try
                    {
                        //新增为空判断
                        val = TransForm.IsNanInfinity(float.Parse(val)).ToString();
                    }
                    catch
                    {
                        val = "-1";
                    }
                    rs = val;
                }
                else
                {
                    rs = "0";
                }
            }
            else if ("INT".Equals(type))
            {
                if (TransForm.IsInt(val))
                {
                    try
                    {
                        int tmpI;
                        long tmpL;
                        if (int.TryParse(val, out tmpI))
                        {
                            val = tmpI.ToString();
                        }
                        else if (Int64.TryParse(val, out tmpL))
                        {
                            val = tmpL.ToString();
                        }
                        else
                        {
                            val = "0";
                        }
                    }
                    catch
                    {
                        val = "0";
                    }
                    rs = val;
                }
                else
                {
                    rs = "0";
                }
            }
            else if ("BIGINT".Equals(type))
            {
                if (TransForm.IsInt(val))
                {
                    try
                    {
                        val = long.Parse(val).ToString();
                    }
                    catch
                    {
                        val = "0";
                    }
                    rs = val;
                }
                else
                {
                    rs = "0";
                }
            }
            else
            {
                rs = val.Replace("'", "").Replace("", " ").Replace("  ", " ").Replace("\0", " ").Replace("\\", "").Replace(@"\r", "").Replace(@"\t", " ").Replace(@"\n", " ").Replace(@"\f", "").Replace(@"\b", "").Replace(@"\0", "");
            }
            return rs;
        }

        /// <summary>
        /// 获取缓存数据
        /// </summary>
        /// <param name="redis"></param>
        /// <param name="batch"></param>
        /// <param name="positionId"></param>
        /// <param name="stationId"></param>
        /// <returns></returns>
        private HisDataCache GetDevData(RedisHelper redis, IBatch batch, string positionId, string stationId)
        {
            HisDataCache hisDataCache = new HisDataCache();
            try
            {
                Dictionary<string, Object> taskList = new();
                string dataKey = "DATA_" + positionId;
                string alarmKey = "realAlarm_" + stationId;
                taskList.TryAdd(dataKey, batch.MHashGetAllAsync(dataKey));
                taskList.TryAdd(alarmKey, batch.MStringGetAsync(alarmKey));
                batch.Execute();

                Dictionary<string, string> stationDatas = ((Task<HashEntry[]>)taskList[dataKey]).Result.ToDictionary(key => key.Name.ToString(), value => redis.ConvertObj<string>(value.Value));
                Dictionary<string, RealAlarmModel> stationAlarm = redis.ConvertObj<Dictionary<string, RealAlarmModel>>(((Task<RedisValue>)taskList[alarmKey]).Result.ToString());
                hisDataCache.devDatas = stationDatas;
                hisDataCache.stationAlarm = stationAlarm == null ? null : stationAlarm.Where(alarmIn => alarmIn.Value.GetAlarmPush(loopAlarmPush)).ToDictionary(alarmIn => alarmIn.Key, alarmIn => alarmIn.Value);
            }
            catch { }

            return hisDataCache;
        }

        /// <summary>
        /// 判断设备是否告警
        /// </summary>
        /// <param name="stationAlarm"></param>
        /// <param name="positionId"></param>
        /// <returns></returns>
        private bool CheckDevAlarm(Dictionary<string, RealAlarmModel> stationAlarm, string positionId)
        {
            return stationAlarm == null ? false : stationAlarm.Where(alarmIn => positionId.Equals(alarmIn.Value.alarmInfo["position_id"]) || (alarmIn.Value.alarmInfo.ContainsKey("rposition_id") && positionId.Equals(alarmIn.Value.alarmInfo["rposition_id"]))).ToDictionary(alarmIn => alarmIn.Key, alarmIn => "").Count > 0;
        }

        /// <summary>
        /// 获取数据时间
        /// </summary>
        /// <param name="devDatas"></param>
        /// <returns></returns>
        private DateTime GetLatestTime(Dictionary<string, string> devDatas)
        {
            if (devDatas == null)
            {
                return default(DateTime);
            }

            string val = devDatas.GetValueOrDefault("LATEST_TIME", "");
            return string.IsNullOrEmpty(val) ? default(DateTime) : (DateTime)Convert.ChangeType(val.Replace("\"", ""), typeof(DateTime));
        }
        #endregion

        #region 入库
        /// <summary>
        /// sql执行
        /// </summary>
        private void StoreData()
        {
            while (true)
            {
                try
                {
                    while (sqlCache.Count > 0)
                    {
                        SqlInfo sqlInfo;
                        lock (sqlCache)
                        {
                            sqlInfo = sqlCache.Take();
                        }
                        SaveToDb(sqlInfo);
                        Thread.Sleep(1);
                    }
                }
                catch { }
                finally
                {
                    Thread.Sleep(5);
                }
            }
        }

        /// <summary>
        /// 数据入库
        /// </summary>
        /// <param name="data"></param>
        private void SaveToDb(Object data)
        {
            SqlInfo sqlInfo = (SqlInfo)data;
            try
            {
                DbConnection dbconn = dbConnections[sqlInfo.devType];
                if (dbconn.State != ConnectionState.Open)
                {
                    dbconn.Open();
                }
                DbCommand dbCommand = dbConnection.CreateCommand();
                dbCommand.CommandText = sqlInfo.sql;
                dbCommand.ExecuteNonQuery();

                lock (syncLock)
                {
                    sqlSucc++;
                }
            }
            catch (Exception e)
            {
                if (sqlInfo.times >= maxInsTimes)
                {
                    lock (syncLock)
                    {
                        sqlFail++;
                    }
                    mLogger.LogError("插入数据失败了，" + e.Message + " sql:" + sqlInfo.sql);
                }
                sqlInfo.times++;

                //最多尝试多少次
                if (sqlInfo.times <= maxInsTimes)
                {
                    SaveToDb(data);
                }
            }
        }
        #endregion

        #region 监控系统WEB API接口
        /// <summary>
        /// 针对websocket的token验证
        /// 预留，暂无用
        /// </summary>
        /// <param name="webApiData"></param>
        /// <returns></returns>
        public bool GetValidToken(IWebApiData webApiData)
        {
            bool rs = true;
            try
            {
                //var authHeader = httpContext.Request.Headers["Authorization"];
                //var token = authHeader.FirstOrDefault()?.Split(" ").Last(); // 假设使用Bearer token
                //rs = !string.IsNullOrEmpty(token);

                //获取token，进行验证
                //string token = webApiData.Data.ToString();
            }
            catch
            {
                rs = false;
            }

            return rs;
        }

        /// <summary>
        /// 接收数据处理
        /// 这里主要是接收下行控制命令，包括自动读取设置和客户端下发的命令
        /// </summary>
        /// <param name="webApiDataFull"></param>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public Object GetDataJson(WebApiDataFull webApiDataFull, HttpContext httpContext)
        {
            var sw = Stopwatch.StartNew();
            bool rs = false;
            try
            {
                string ip = httpContext.Connection.RemoteIpAddress == null ? "" : httpContext.Connection.RemoteIpAddress.ToString().ToUpper().Replace("::FFFF:", "");
                ushort port = (ushort)httpContext.Connection.RemotePort;

                //固定只处理某些命令
                if (webApiDataFull.ContentReq.Command.ToUpper().Equals("some_command"))
                {

                }
                else
                {
                    ((WebApiDataRes)webApiDataFull.ContentRes).Code = WebApiStatusCode.code_404;
                    ((WebApiDataRes)webApiDataFull.ContentRes).Message = EnumHelper.GetDescription(((WebApiDataRes)webApiDataFull.ContentRes).Code) + "，无法响应命令【" + webApiDataFull.ContentReq.Command + "】";
                }
            }
            catch (Exception e)
            {
                ((WebApiDataRes)webApiDataFull.ContentRes).Code = WebApiStatusCode.code_5504;
                ((WebApiDataRes)webApiDataFull.ContentRes).Message = EnumHelper.GetDescription(((WebApiDataRes)webApiDataFull.ContentRes).Code) + "," + e.Message;
            }

            return rs;
        }

        /// <summary>
        /// 参数检查
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public WebParamData CheckParam(HttpContext httpContext)
        {
            WebParamData webParamData = new WebParamData();
            string msg = "";

            try
            {
                //路径参数
                string apiver = (httpContext.Request.RouteValues["apiver"] ?? httpContext.Request.Query["apiver"]).ToString();
                string level = (httpContext.Request.RouteValues["level"] ?? httpContext.Request.Query["level"]).ToString();
                string positionid = (httpContext.Request.RouteValues["positionid"] ?? httpContext.Request.Query["positionid"]).ToString();
                string sn = (httpContext.Request.RouteValues["sn"] ?? httpContext.Request.Query["sn"]).ToString();

                if (apiver == null || "".Equals(apiver))
                {
                    msg += "，API协议版本apiver不能为空";
                }

                if (level == null || "".Equals(level))
                {
                    msg += "，级别level（SYSTEM(系统级) DEVICE(设备级)）不能为空";
                }

                if (positionid == null || "".Equals(positionid))
                {
                    msg += "，位置编号（或工位号）positionid不能为空";
                }

                else if (positionid.Split("_").Length < 3)
                {
                    msg += "，位置编号（或工位号）positionid异常，参考格式xx_xxx_xxx";
                }

                if (sn == null || "".Equals(sn))
                {
                    msg += "，系统/设备序列号sn不能为空";
                }

                webParamData.apiver = apiver;
                webParamData.level = level;
                webParamData.positionid = positionid;
                webParamData.sn = sn;
            }
            catch (Exception e)
            {
                msg += e.Message;
            }
            finally
            {
                webParamData.nowmal = string.IsNullOrEmpty(msg);
                webParamData.msg = webParamData.nowmal ? msg : msg.Substring(1);
            }

            return webParamData;
        }

        /// <summary>
        /// 检查请求参数
        /// </summary>
        /// <param name="data"></param>
        /// <param name="webApiDataRes"></param>
        /// <returns></returns>
        public bool CheckData(string data, WebApiDataFull webApiDataFull)
        {
            bool rs = false;
            string msg = "";
            try
            {
                IWebApiData webApiData = null;
                Dictionary<string, Object> dictData = null;
                if (data != null && !"".Equals(data.ToString()) && ValidJson.IsJson(data.ToString()))
                {
                    dictData = JsonConvert.DeserializeObject<Dictionary<string, Object>>(data.ToString());
                    if (webApiDataFull.IsWebSocket)
                    {
                        webApiData = JsonConvert.DeserializeObject<WebApiDataReqWS>(data.ToString());
                    }
                    else
                    {
                        webApiData = JsonConvert.DeserializeObject<WebApiDataReq>(data.ToString());
                    }

                    if (webApiData == null)
                    {
                        msg += "，数据异常";
                    }
                }
                else
                {
                    msg += "，数据异常";
                }

                //数据必须参数检查
                if (dictData != null && dictData.Count > 0)
                {
                    Dictionary<string, string> tmpDict = dictData.ToDictionary(d => d.Key.ToUpper(), d => "");
                    if (!tmpDict.ContainsKey("REQUESTID"))
                    {
                        msg += "，数据不完整，缺少RequestId（请求编号）参数";
                    }
                    if (!tmpDict.ContainsKey("COMMAND"))
                    {
                        msg += "，数据不完整，缺少Command（命令）参数";
                    }
                    if (!tmpDict.ContainsKey("VERSION"))
                    {
                        msg += "，数据不完整，缺少Version（设备或系统通信协议版本）参数";
                    }
                    if (!tmpDict.ContainsKey("DATA"))
                    {
                        msg += "，数据不完整，缺少Data数据";
                    }
                    if (!tmpDict.ContainsKey("TIMESTAMP"))
                    {
                        msg += "，数据不完整，缺少Timestamp（时间戳）参数";
                    }
                    if (webApiDataFull.IsWebSocket && !tmpDict.ContainsKey("TYPE"))
                    {
                        msg += "，数据不完整，缺少Type（消息类型）参数";
                    }
                }

                webApiDataFull.dictData = dictData;
                webApiDataFull.ContentReq = webApiData;

                //消息类型判断
                if (webApiDataFull.IsWebSocket && webApiData != null && !EnumHelper.HasValue<WebApiTypeCodeWS>(((WebApiDataReqWS)webApiDataFull.ContentReq).Type))
                {
                    msg += "，数据不完整，Type（消息类型）异常";
                }

                //工位号格式处理，确保格式统一
                webApiDataFull.webParamData.positionid = webApiDataFull.webParamData.positionid.Replace("_0X", "_0x");
                rs = string.IsNullOrEmpty(msg);
                if (webApiDataFull.IsWebSocket)
                {
                    ((WebApiDataResWS)webApiDataFull.ContentRes).Code = rs ? WebApiStatusCode.code_200 : WebApiStatusCode.code_400;
                }
                else
                {
                    ((WebApiDataRes)webApiDataFull.ContentRes).Code = rs ? WebApiStatusCode.code_200 : WebApiStatusCode.code_400;
                }
            }
            catch (Exception e)
            {
                rs = false;
                //数据处理从错误
                msg += "，" + e.Message;
                if (webApiDataFull.IsWebSocket)
                {
                    ((WebApiDataResWS)webApiDataFull.ContentRes).Code = WebApiStatusCode.code_5504;
                }
                else
                {
                    ((WebApiDataRes)webApiDataFull.ContentRes).Code = WebApiStatusCode.code_5504;
                }
            }
            finally
            {
                try
                {
                    if (webApiDataFull.IsWebSocket)
                    {
                        ((WebApiDataResWS)webApiDataFull.ContentRes).Message = EnumHelper.GetDescription(((WebApiDataResWS)webApiDataFull.ContentRes).Code) + msg;
                    }
                    else
                    {
                        ((WebApiDataRes)webApiDataFull.ContentRes).Message = EnumHelper.GetDescription(((WebApiDataRes)webApiDataFull.ContentRes).Code) + msg;
                    }

                    if (webApiDataFull.ContentReq != null)
                    {
                        webApiDataFull.ContentRes.RequestId = webApiDataFull.ContentReq.RequestId;
                        webApiDataFull.ContentRes.Command = webApiDataFull.ContentReq.Command;
                        webApiDataFull.ContentRes.Version = webApiDataFull.ContentReq.Version;
                        if (webApiDataFull.IsWebSocket)
                        {
                            if (rs)
                            {
                                ((WebApiDataResWS)webApiDataFull.ContentRes).Type = ((WebApiDataReqWS)webApiDataFull.ContentReq).Type.ToLower();
                            }
                            else
                            {
                                ((WebApiDataResWS)webApiDataFull.ContentRes).Type = WebApiTypeCodeWS.error.ToString();
                            }
                        }
                    }
                }
                catch
                {
                    rs = false;
                    //服务异常失败
                    if (webApiDataFull.IsWebSocket)
                    {
                        ((WebApiDataResWS)webApiDataFull.ContentRes).Code = WebApiStatusCode.code_5503;
                        ((WebApiDataResWS)webApiDataFull.ContentRes).Message = EnumHelper.GetDescription(((WebApiDataResWS)webApiDataFull.ContentRes).Code);
                    }
                    else
                    {
                        ((WebApiDataRes)webApiDataFull.ContentRes).Code = WebApiStatusCode.code_5503;
                        ((WebApiDataRes)webApiDataFull.ContentRes).Message = EnumHelper.GetDescription(((WebApiDataRes)webApiDataFull.ContentRes).Code);
                    }
                }
            }

            return rs;
        }
        #endregion

        /// <summary>
        /// 获取客户端IP
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public string GetClientIp(HttpContext httpContext)
        {
            string ip = "";
            try
            {
                int agentType = GetAgentType(httpContext);
                //web版本访问时，由于科江使用了代理，特定传输了这两个参数
                if (agentType == 2)
                {
                    ip = httpContext.Request.Headers["X-Real-IP"].ToString();
                    if (string.IsNullOrEmpty(ip))
                    {
                        httpContext.Request.Headers["X-Forwarded-For"].ToString();
                    }
                    if (string.IsNullOrEmpty(ip))
                    {
                        ip = httpContext.Connection.RemoteIpAddress == null ? "" : httpContext.Connection.RemoteIpAddress.ToString().ToUpper().Replace("F", "").Replace(":", "");
                    }
                }
                else
                {
                    ip = httpContext.Connection.RemoteIpAddress == null ? "" : httpContext.Connection.RemoteIpAddress.ToString().ToUpper().Replace("F", "").Replace(":", "");
                }
            }
            catch (Exception e)
            { }

            return ip;
        }

        /// <summary>
        /// 获取客户端类型
        /// 0-是测试的Apifox或者Postman
        /// 1-是cs客户端（即User-Agent为空串）
        /// 2-是bs客户端
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public int GetAgentType(HttpContext httpContext)
        {
            int agentType = 0;
            try
            {
                string agent = httpContext.Request.Headers["User-Agent"].ToString();

                //是否cs客户端，用以区分是否cs客户端，因为web客户端的布局和cs客户端的不一样，需要区别开来
                //cs客户端过来的agent为空串
                //POSTMAN和APIFOX是测试
                //CSCLIENT是和客户端约定的字符串
                //agentType = (agent.ToUpper().Contains("POSTMAN") || agent.ToUpper().Contains("APIFOX")) ? 0 : ("CSCLIENT".Equals(agent.ToUpper()) ? 1 : 2);
                agentType = (agent.ToUpper().Contains("POSTMAN") || agent.ToUpper().Contains("APIFOX")) ? 0 : ("".Equals(agent.ToUpper()) ? 1 : 2);
            }
            catch (Exception e)
            { }

            return agentType;
        }

        #region 历史数据获取
        /// <summary>
        /// 历史数据获取
        /// </summary>
        /// <param name="positionId"></param>
        /// <param name="beginTime"></param>
        /// <param name="endTime"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public CommonDataRes GetHisData(string data, HttpContext httpContext)
        {
            CommonDataRes commonDataRes = new CommonDataRes();
            try
            {
                Dictionary<string, string> param = JsonConvert.DeserializeObject<Dictionary<string, string>>(data).ToDictionary(d => d.Key.ToUpper(), d => d.Value);
                string positionId = param.GetValueOrDefault("POSITION_ID", "");
                string beginTime = param.GetValueOrDefault("BEGIN_TIME", "");
                string endTime = param.GetValueOrDefault("END_TIME", "");
                int pageIndex = int.Parse(param.GetValueOrDefault("PAGE_INDEX", "1"));
                int pageSize = int.Parse(param.GetValueOrDefault("PAGE_SIZE", "20"));

                if (string.IsNullOrEmpty(positionId))
                {
                    commonDataRes.Code = WebApiStatusCode.code_400;
                    commonDataRes.Message = "position_id参数为空";
                    return commonDataRes;
                }

                if (string.IsNullOrEmpty(beginTime))
                {
                    commonDataRes.Code = WebApiStatusCode.code_400;
                    commonDataRes.Message = "begin_time参数为空";
                    return commonDataRes;
                }

                if (string.IsNullOrEmpty(endTime))
                {
                    commonDataRes.Code = WebApiStatusCode.code_400;
                    commonDataRes.Message = "end_time参数为空";
                    return commonDataRes;
                }

                if (!TransForm.IsDate(beginTime) || !TransForm.IsDate(endTime))
                {
                    commonDataRes.Code = WebApiStatusCode.code_400;
                    commonDataRes.Message = "开始或结束时间非法";
                    return commonDataRes;
                }

                string tableName = "his_" + positionId.ToLower();
                if (!CheckTableExists(tableName))
                {
                    commonDataRes.Code = WebApiStatusCode.code_400;
                    commonDataRes.Message = "position_id参数错误，表【" + tableName + "】不存在";
                    return commonDataRes;
                }

                if (!IsDbConn)
                {
                    commonDataRes.Code = WebApiStatusCode.code_501;
                    commonDataRes.Message = "数据库无法连接";
                    return commonDataRes;
                }

                string devType = positionId.Split('_')[1].ToLower();
                if (hisSetDetail.ContainsKey(devType))
                {
                    string cols = "id, get_time 时间, serial_no 序列号, dev_alarm 告警";
                    List<Dictionary<string, string>> tableDetail = hisSetDetail[devType];
                    foreach (var colInfo in tableDetail)
                    {
                        string colName = colInfo.GetValueOrDefault("col_name", "");
                        string colDesc = colInfo.GetValueOrDefault("col_desc", "");
                        colDesc = colDesc.Replace("+", "加");

                        cols = string.Format(cols + ",{0} {1}", colName, colDesc);
                    }

                    string where = $" and get_time between '{TransForm.DateTimeFormat(DateTime.Parse(beginTime))}' and '{TransForm.DateTimeFormat(DateTime.Parse(endTime))}'";
                    string querySql = string.Format("select {0} from {1} where 1=1 {2} order by id desc", cols, tableName, where);
                    //commonDataRes.Data = DbUtil.ExecReader(dbConnection, querySql);

                    //分页信息
                    int pgIndex = pageIndex <= 0 ? 1 : pageIndex;
                    int pgSize = pageSize <= 0 ? 20 : pageSize;
                    PageResult ps = PageUtil.GetPageDatas(dbConnection, querySql, new Page() { pageIndex = pgIndex, pageSize = pgSize });
                    commonDataRes.Data = ps;
                }
                else
                {
                    commonDataRes.Code = WebApiStatusCode.code_400;
                    commonDataRes.Message = "设备类型【" + devType + "】参数不存在";
                    return commonDataRes;
                }
            }
            catch (Exception e)
            { 
            
            }
            return commonDataRes;        
        }
        #endregion

        /// <summary>
        /// 通用状态返回
        /// </summary>
        /// <returns></returns>
        public CommonDataRes GetStatus()
        {
            CommonDataRes commonDataRes = new CommonDataRes();

            Dictionary<string, Object> status = new();
            status.TryAdd("wsCount", WebsocketClientManager.websocketClient.GetCount());
            status.TryAdd("sqlCount", sqlCache.Count);
            commonDataRes.Data = status;

            return commonDataRes;
        }

        #region 日志打印

        /// <summary>
        /// 定时打印提示信息
        /// 定时重新初始化
        /// </summary>
        private void TimerTask()
        {
            int times = 0;
            Thread.Sleep(10 * 1000);
            while (true)
            {
                mLogger.LogInformation(nodeName +
                    " 线程数=" + ThreadPool.ThreadCount +
                    " WS连接=" + WebsocketClientManager.websocketClient.GetCount() +
                    " 入库等待=" + sqlCache.Count + " 成功=" + sqlSucc + " 失败=" + sqlFail + (IsDbConn ? "" : " 数据库连接失败"));

                times++;
                if (times > 10000 * 1000)
                {
                    times = 1;
                }
                Thread.Sleep(10 * 1000);
            }
        }
        #endregion
    }
}
